#include <iostream>
#include <functional>
#include <vector>
#include <memory>
#include <unistd.h>
#include <unordered_map>

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;

// 定时器任务类
class TimerTask
{
private:
    uint64_t _id;           // 定时器任务对象ID
    uint32_t _timeout;      // 定时任务的超时时间
    bool _canceled;         // 任务是否被取消
    TaskFunc _task_cb;      // 定时器对象要执行的定时任务
    ReleaseFunc _release;   // 用于删除 TimerWheel 中保存的定时器对象信息 (也是在析构的时候被调用)
public:
    TimerTask(uint64_t id, uint32_t delay, const TaskFunc &cb) : _id(id), _timeout(delay), _task_cb(cb), _canceled(false) {}

    void Cancel() { _canceled = true; }
    void SetRelease(const ReleaseFunc &cb) { _release = cb; }
    uint32_t DelayTime() { return _timeout; }

    // 析构的时候执行任务
    ~TimerTask() 
    { 
        if(_canceled == false) _task_cb(); 
        _release(); 
    }  
};

#define MAX_DELAY 60

// 时间轮
class TimerWheel
{
private:
    using WeakTask = std::weak_ptr<TimerTask>;        // 二次添加同一个任务对象 要找到同一个计数器
    using PtrTask = std::shared_ptr<TimerTask>;
    int _tick;                                        // 当前的秒针，走到哪里就释放哪里的对象 (执行哪里的任务)
    int _capacity;                                    // 表盘最大数量 -- 最大延迟时间
    std::vector<std::vector<PtrTask>> _wheel;         // 时间轮: 存放智能指针类型 引用计数为 0 执行任务
    std::unordered_map<uint64_t, WeakTask> _timers;   // 所有定时器的 weak_ptr 对象 构造出新的 share_ptr 共享计数
private:
    void RemoveTimer(uint64_t id) {
        auto it = _timers.find(id);
        if(it != _timers.end()) {
            _timers.erase(it);
        }
    }
public:
    TimerWheel() : _capacity(MAX_DELAY), _tick(0), _wheel(_capacity) {}
    // 添加定时器任务
    void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb) { 
        PtrTask pt = std::make_shared<TimerTask>(id, delay, cb);
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));               // 任务被销毁的时候需要从 wheel 中删除
        _timers[id] = WeakTask(pt);
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);
    }
    // 刷新 (延迟) 定时器任务
    void TimerRefresh(uint64_t id) { 
        // 通过保存的定时器对象的 weak_ptr 然后实例化一个 shared_ptr 出来，添加到 wheel中 这样就是增加同一个引用计数了
        auto it = _timers.find(id);
        if(it == _timers.end()) {
            return ;    // 没找到定时任务，没法延时
        }
        PtrTask pt = it->second.lock();     // lock 获取 weak_ptr 管理对象对应的 shared_ptr
        int delay = pt->DelayTime();        // 获取到定时任务初始的延迟时间
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);
    }       
    // 取消任务
    void TimerCancel(uint64_t id) {
        auto it = _timers.find(id);
        if(it == _timers.end()) {
            return ;    // 没找到定时任务，没法延时
        }
        PtrTask pt = it->second.lock();
        if(pt) pt->Cancel();
    }
    // 这个函数应该每秒钟执行一次，相当于秒针向后走了一步
    void RunTimerTask() {
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear();   // 清空指定位置的数组，就会把数组中保存的所有定时器对象释放掉 计数--
    }
};

class Test {
    public:
        Test() { std::cout << "构造" << std::endl; }
        ~Test() { std::cout << "析构" << std::endl; }
};

void DelTest(Test *t) {
    delete t;
}

int main()
{
    TimerWheel tw;

    Test *t = new Test();

    tw.TimerAdd(888, 5, std::bind(DelTest, t));

    for(int i = 0; i < 5; i++) {
        sleep(1);
        tw.TimerRefresh(888);   // 刷新定时任务 
        tw.RunTimerTask();      // 向后移动秒针
        std::cout << "刷新了一下定时任务，需要5s钟后才会销毁\n";
    }

    // 测试取消
    tw.TimerCancel(888);

    while(true) {
        sleep(1);
        std::cout << "-----------------------\n";  
        tw.RunTimerTask();      // 向后移动秒针
    }

    return 0;
}